//
//	(c) 2009 Andrew Kalmacky
//
#include "../logger/logger.h"
#include "ltm.h"
#include <list>
#include <map>

namespace ak
{
    namespace ltm
    {
        typedef std::list<Object**> weak_ptrs_t;
        typedef std::map<const Object*, Object*> weak_xlat_t;

        TLS_VAR(weak_ptrs_t* _weak_ptrs) = NULL;
        TLS_VAR(weak_xlat_t* _weak_xlat) = NULL; // mapping original->copy
        TLS_VAR(bool _nested_copy) = false;
        TLS_VAR(bool in_move) = false;
    }
}

using namespace ak::ltm;

copy_solver::copy_solver()
{
    _weak_ptrs = new weak_ptrs_t;
    _weak_xlat = new weak_xlat_t;
}
copy_solver::~copy_solver()
{
    delete _weak_ptrs;
    _weak_ptrs = NULL;
    delete _weak_xlat;
    _weak_xlat = NULL;
}

bool Object::_acquire_ownership() const no_throw
{
        size_t prev_val = _ptr_counter.load(tm::memory_order_relaxed);
    for (;;)
    {
        if (prev_val & OBJ_OWNED)
            return false;
        if (_ptr_counter.compare_exchange_strong(prev_val, (prev_val + 1) | OBJ_OWNED, tm::memory_order_acquire))
            return true;
        ak::relax_cpu();
    }
}

void Object::_unlock() no_throw
{
    if (this)
    {
        --_ptr_counter;
        if (is_ptr_counter_empty())
            delete_this();
    }
}

void Object::_copy_to_ref(Object*& dst) const
{
    if (!this)
        dst = NULL;
    else if (_acquire_ownership())
        dst = const_cast<Object*>(this);
    else if (!_nested_copy)
    {
        _nested_copy = true;
        this->copy(dst);
        if (dst != this)
        {
            (*_weak_xlat)[this] = dst;
            dst->_ptr_counter = OBJ_OWNED | 1;
        }
        for (weak_ptrs_t::iterator i = _weak_ptrs->begin(); i != _weak_ptrs->end(); ++i)
        {
            weak_xlat_t::iterator dst = _weak_xlat->find(**i);
            if (dst != _weak_xlat->end())
                **i = dst->second;
        }
        _weak_ptrs->clear();
        _weak_xlat->clear();
        _nested_copy = false;
    }
    else
    {
        this->copy(dst);
        if (dst != this)
        {
            (*_weak_xlat)[this] = dst;
            dst->_ptr_counter = OBJ_OWNED | 1;
        }
    }
}

void Object::delete_this()
{
    if (_can_be_deleted())
        delete this;
}

Object* Object::_inner_get_weak() const
{
        WeakBlock* w = _weak.load(tm::memory_order_acquire);
    if (w)
        return w->_lock();
    WeakBlock* new_w = new WeakBlock(const_cast<Object*>(this));
    new_w->_lock();
    if (_weak.compare_exchange_strong(w, new_w))
    {
        return new_w->_lock();
    }
    delete new_w;
        return _weak.load(tm::memory_order_acquire)->_lock();
}

Object* Object::_inner_get_target() const
{
    return const_cast<Object*>(this->_lock());
}

bool Object::_can_be_deleted()
{
    if (!_weak)
        return true;
    ak::atomic_lock<Object> target(_weak->_weak);
    if (!is_ptr_counter_empty())
        return false;
    target = NULL;
    return true;
}

void WeakBlock::copy(Object*& dst) const
{
    _weak_ptrs->push_back(&dst);
    dst = const_cast<Object*>(_lock());
}

Object* WeakBlock::_inner_get_weak() const
{
    return const_cast<Object*>(_lock());
}

Object* WeakBlock::_inner_get_target() const
{
    ak::atomic_lock<WeakBlock> target(_weak);
    return target->_lock();
}

void WeakBlock::delete_this()
{
    // weak blocks use _weak field in uncommon way so no _can_be_deleted() called
    delete this;
}

void SharedObject::copy(Object*& dst) const
{
    dst = copy_as_shared();
}
